#pragma once
#include "VulkanCommandBuffer.h"
#include "VulkanFrameBuffer.h"
#include "VulkanRenderTarget.h"
#include "VulkanResource.h"
#include "Runtime/Platform/ScopeRWLock.h"

namespace Alice
{
    class VulkanDevice;
	// Merge a depth and a stencil layout for drivers that don't support VK_KHR_separate_depth_stencil_layouts
	inline VkImageLayout GetMergedDepthStencilLayout(VkImageLayout DepthLayout, VkImageLayout StencilLayout)
	{
		if ((DepthLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) || (StencilLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL))
		{
			return VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
		}

		if ((DepthLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) || (StencilLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL))
		{
			return VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
		}

		if ((DepthLayout == VK_IMAGE_LAYOUT_UNDEFINED) && (StencilLayout == VK_IMAGE_LAYOUT_UNDEFINED))
		{
			return VK_IMAGE_LAYOUT_UNDEFINED;
		}

		// Depth formats used on textures that aren't targets (like GBlackTextureDepthCube)
		if ((DepthLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) && (StencilLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL))
		{
			return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
		}

		auto IsMergedLayout = [](VkImageLayout Layout)
		{
			return	(Layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) ||
					(Layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL) ||
					(Layout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL) ||
					(Layout == VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL);
		};

		if (IsMergedLayout(DepthLayout) || IsMergedLayout(StencilLayout))
		{
			return DepthLayout;
		}

		if (DepthLayout == VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL)
		{
			if ((StencilLayout == VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL) || (StencilLayout == VK_IMAGE_LAYOUT_UNDEFINED))
			{
				return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
			}
			else
			{
				return VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL;
			}
		}
		else if (DepthLayout == VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL)
		{
			if ((StencilLayout == VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL) || (StencilLayout == VK_IMAGE_LAYOUT_UNDEFINED))
			{
				return VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
			}
			else
			{
				return VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL;
			}
		}
		else
		{
			return (StencilLayout == VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL) ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
		}
	}
	static bool VulkanFormatHasStencil(VkFormat Format)
	{
		switch (Format)
		{
		case VK_FORMAT_D16_UNORM_S8_UINT:
		case VK_FORMAT_D24_UNORM_S8_UINT:
		case VK_FORMAT_D32_SFLOAT_S8_UINT:
		case VK_FORMAT_S8_UINT:
			return true;
		default:
			return false;
		}
	}
	template <typename TAttachmentReferenceType>
	struct VulkanAttachmentReference : public TAttachmentReferenceType
	{
		VulkanAttachmentReference()
		{
			ZeroStruct();
		}

		VulkanAttachmentReference(const VkAttachmentReference& AttachmentReferenceIn, VkImageAspectFlags AspectMask)
		{
			SetAttachment(AttachmentReferenceIn, AspectMask);
		}

		inline void SetAttachment(const VkAttachmentReference& AttachmentReferenceIn, VkImageAspectFlags AspectMask) {  }
		inline void SetAttachment(const VulkanAttachmentReference<TAttachmentReferenceType>& AttachmentReferenceIn, VkImageAspectFlags AspectMask) { *this = AttachmentReferenceIn; }
		inline void SetDepthStencilAttachment(const VkAttachmentReference& AttachmentReferenceIn, const VkAttachmentReferenceStencilLayout* StencilReference, VkImageAspectFlags AspectMask, bool bSupportsParallelRendering) {  }
		inline void ZeroStruct() {}
		inline void SetAspect(uint32 Aspect) {}
	};
	template <typename TSubpassDescriptionType>
class FVulkanSubpassDescription
	{
	};

	template<>
	struct FVulkanSubpassDescription<VkSubpassDescription>
		: public VkSubpassDescription
	{
		FVulkanSubpassDescription()
		{
			memset(this,0,sizeof(VkSubpassDescription));
			pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
		}

		void SetColorAttachments(const std::vector<VulkanAttachmentReference<VkAttachmentReference>>& ColorAttachmentReferences, int OverrideCount = -1)
		{
			colorAttachmentCount = (OverrideCount == -1) ? ColorAttachmentReferences.size() : OverrideCount;
			pColorAttachments = ColorAttachmentReferences.data();
		}

		void SetResolveAttachments(const std::vector<VulkanAttachmentReference<VkAttachmentReference>>& ResolveAttachmentReferences)
		{
			if (ResolveAttachmentReferences.size() > 0)
			{
				pResolveAttachments = ResolveAttachmentReferences.data();
			}
		}

		void SetDepthStencilAttachment(VulkanAttachmentReference<VkAttachmentReference>* DepthStencilAttachmentReference)
		{
			pDepthStencilAttachment = static_cast<VkAttachmentReference*>(DepthStencilAttachmentReference);
		}

		void SetInputAttachments(VulkanAttachmentReference<VkAttachmentReference>* InputAttachmentReferences, uint32 NumInputAttachmentReferences)
		{
			pInputAttachments = static_cast<VkAttachmentReference*>(InputAttachmentReferences);
			inputAttachmentCount = NumInputAttachmentReferences;
		}

		void SetDepthStencilResolveAttachment(VkSubpassDescriptionDepthStencilResolveKHR* DepthStencilResolveAttachmentDesc)
		{
			// No-op without VK_KHR_create_renderpass2
		}

		void SetShadingRateAttachment(VkFragmentShadingRateAttachmentInfoKHR* /* ShadingRateAttachmentInfo */)
		{
			// No-op without VK_KHR_create_renderpass2
		}

		void SetMultiViewMask(uint32_t Mask)
		{
			// No-op without VK_KHR_create_renderpass2
		}
	};
	template <>
	inline void VulkanAttachmentReference<VkAttachmentReference>::SetAttachment(const VkAttachmentReference& AttachmentReferenceIn, VkImageAspectFlags AspectMask)
	{
		attachment = AttachmentReferenceIn.attachment;
		layout = AttachmentReferenceIn.layout;
	}

	template <>
	inline void VulkanAttachmentReference<VkAttachmentReference2>::SetAttachment(const VkAttachmentReference& AttachmentReferenceIn, VkImageAspectFlags AspectMask)
	{
		sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2;
		pNext = nullptr;
		attachment = AttachmentReferenceIn.attachment;
		layout = AttachmentReferenceIn.layout;
		aspectMask = AspectMask;
	}

	template<>
	inline void VulkanAttachmentReference<VkAttachmentReference2>::SetAttachment(const VulkanAttachmentReference<VkAttachmentReference2>& AttachmentReferenceIn, VkImageAspectFlags AspectMask)
	{
		sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2;
		pNext = nullptr;
		attachment = AttachmentReferenceIn.attachment;
		layout = AttachmentReferenceIn.layout;
		aspectMask = AspectMask;
	}

	template <>
	inline void VulkanAttachmentReference<VkAttachmentReference>::SetDepthStencilAttachment(const VkAttachmentReference& AttachmentReferenceIn, 
		const VkAttachmentReferenceStencilLayout* StencilReference, VkImageAspectFlags AspectMask, bool bSupportsParallelRendering)
	{
		attachment = AttachmentReferenceIn.attachment;
		const VkImageLayout StencilLayout = StencilReference ? StencilReference->stencilLayout : VK_IMAGE_LAYOUT_UNDEFINED;
		layout = GetMergedDepthStencilLayout(AttachmentReferenceIn.layout, StencilLayout);
	}

	template <>
	inline void VulkanAttachmentReference<VkAttachmentReference2>::SetDepthStencilAttachment(const VkAttachmentReference& AttachmentReferenceIn, 
		const VkAttachmentReferenceStencilLayout* StencilReference, VkImageAspectFlags AspectMask, bool bSupportsParallelRendering)
	{
		sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2;
		pNext = (bSupportsParallelRendering && StencilReference && StencilReference->stencilLayout != VK_IMAGE_LAYOUT_UNDEFINED) ? StencilReference : nullptr;
		attachment = AttachmentReferenceIn.attachment;
		layout = bSupportsParallelRendering ? AttachmentReferenceIn.layout : GetMergedDepthStencilLayout(AttachmentReferenceIn.layout, StencilReference->stencilLayout);
		aspectMask = AspectMask;
	}


	template<>
	inline void VulkanAttachmentReference<VkAttachmentReference>::ZeroStruct()
	{
		attachment = 0;
		layout = VK_IMAGE_LAYOUT_UNDEFINED;
	}

	template<>
	inline void VulkanAttachmentReference<VkAttachmentReference2>::ZeroStruct()
	{
		sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2;
		pNext = nullptr;
		attachment = 0;
		layout = VK_IMAGE_LAYOUT_UNDEFINED;
		aspectMask = 0;
	}

	template<>
	inline void VulkanAttachmentReference<VkAttachmentReference2>::SetAspect(uint32 Aspect)
	{
		aspectMask = Aspect;
	}
	template <typename TSubpassDependencyType>
	struct FVulkanSubpassDependency : public TSubpassDependencyType
	{
	};

	template<>
	struct FVulkanSubpassDependency<VkSubpassDependency>
		: public VkSubpassDependency
	{
		FVulkanSubpassDependency()
		{
			memset(this,0, sizeof(VkSubpassDependency));
		}
	};

	template<>
	struct FVulkanSubpassDependency<VkSubpassDependency2>
		: public VkSubpassDependency2
	{
		FVulkanSubpassDependency()
		{
			memset(this,0,sizeof(VkSubpassDependency2));
			sType=VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2;
			viewOffset = 0;		// According to the Vulkan spec: "If dependencyFlags does not include VK_DEPENDENCY_VIEW_LOCAL_BIT, viewOffset must be 0"
		}
	};
	template<typename TAttachmentDescriptionType>
	struct VulkanAttachmentDescription
	{
	};

	template<>
	struct VulkanAttachmentDescription<VkAttachmentDescription>
		: public VkAttachmentDescription
	{
		VulkanAttachmentDescription()
		{
			memset(this,0, sizeof(VkAttachmentDescription));
		}

		VulkanAttachmentDescription(const VkAttachmentDescription& InDesc)
		{
			flags = InDesc.flags;
			format = InDesc.format;
			samples = InDesc.samples;
			loadOp = InDesc.loadOp;
			storeOp = InDesc.storeOp;
			stencilLoadOp = InDesc.stencilLoadOp;
			stencilStoreOp = InDesc.stencilStoreOp;
			initialLayout = InDesc.initialLayout;
			finalLayout = InDesc.finalLayout;
		}

		VulkanAttachmentDescription(const VkAttachmentDescription& InDesc, const VkAttachmentDescriptionStencilLayout* InStencilDesc, bool bSupportsParallelRendering)
		{
			flags = InDesc.flags;
			format = InDesc.format;
			samples = InDesc.samples;
			loadOp = InDesc.loadOp;
			storeOp = InDesc.storeOp;
			stencilLoadOp = InDesc.stencilLoadOp;
			stencilStoreOp = InDesc.stencilStoreOp;

			const bool bHasStencilLayout = VulkanFormatHasStencil(InDesc.format) && (InStencilDesc != nullptr);
			const VkImageLayout StencilInitialLayout = bHasStencilLayout ? InStencilDesc->stencilInitialLayout : VK_IMAGE_LAYOUT_UNDEFINED;
			initialLayout = GetMergedDepthStencilLayout(InDesc.initialLayout, StencilInitialLayout);
			const VkImageLayout StencilFinalLayout = bHasStencilLayout ? InStencilDesc->stencilFinalLayout : VK_IMAGE_LAYOUT_UNDEFINED;
			finalLayout = GetMergedDepthStencilLayout(InDesc.finalLayout, StencilFinalLayout);
		}
	};

	template<>
	struct VulkanAttachmentDescription<VkAttachmentDescription2>
		: public VkAttachmentDescription2
	{
		VulkanAttachmentDescription()
		{
			memset(this,0,sizeof(VkAttachmentDescription2));
			sType=VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2;
		}

		VulkanAttachmentDescription(const VkAttachmentDescription& InDesc)
		{
			sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2;
			pNext = nullptr;
			flags = InDesc.flags;
			format = InDesc.format;
			samples = InDesc.samples;
			loadOp = InDesc.loadOp;
			storeOp = InDesc.storeOp;
			stencilLoadOp = InDesc.stencilLoadOp;
			stencilStoreOp = InDesc.stencilStoreOp;
			initialLayout = InDesc.initialLayout;
			finalLayout = InDesc.finalLayout;
		}

		VulkanAttachmentDescription(const VkAttachmentDescription& InDesc, const VkAttachmentDescriptionStencilLayout* InStencilDesc, bool bSupportsParallelRendering)
		{
			const bool bHasStencilLayout = bSupportsParallelRendering && VulkanFormatHasStencil(InDesc.format) && (InStencilDesc != nullptr);

			sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2;
			pNext = (bHasStencilLayout && (InStencilDesc->stencilFinalLayout != VK_IMAGE_LAYOUT_UNDEFINED)) ? InStencilDesc : nullptr;
			flags = InDesc.flags;
			format = InDesc.format;
			samples = InDesc.samples;
			loadOp = InDesc.loadOp;
			storeOp = InDesc.storeOp;
			stencilLoadOp = InDesc.stencilLoadOp;
			stencilStoreOp = InDesc.stencilStoreOp;
			initialLayout = bSupportsParallelRendering ? InDesc.initialLayout : GetMergedDepthStencilLayout(InDesc.initialLayout, InStencilDesc->stencilInitialLayout);
			finalLayout = bSupportsParallelRendering ? InDesc.finalLayout : GetMergedDepthStencilLayout(InDesc.finalLayout, InStencilDesc->stencilFinalLayout);
		}
	};
	template <typename T>
	struct FVulkanRenderPassCreateInfo
	{};

	template<>
	struct FVulkanRenderPassCreateInfo<VkRenderPassCreateInfo> : public VkRenderPassCreateInfo
	{
		FVulkanRenderPassCreateInfo()
		{
			memset(this,0,sizeof(VkRenderPassCreateInfo));
			sType=VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
		}

		void SetCorrelationMask(const uint32_t* MaskPtr)
		{
			// No-op without VK_KHR_create_renderpass2
		}

		VkRenderPass Create(VulkanDevice& Device)
		{
			VkRenderPass Handle = VK_NULL_HANDLE;
			vkCreateRenderPass(Device.GetDevice(), this, NULL, &Handle);
			return Handle;
		}
	};
    template <typename TSubpassDescriptionClass, typename TSubpassDependencyClass, typename TAttachmentReferenceClass, typename TAttachmentDescriptionClass, typename TRenderPassCreateInfoClass>
	class VulkanRenderPassBuilder
	{
	public:
		VulkanRenderPassBuilder(VulkanDevice& InDevice)
			: Device(InDevice)
			, CorrelationMask(0)
		{}

		void BuildCreateInfo(const VulkanRenderTargetLayout& RTLayout)
		{
			uint32 NumSubpasses = 0;
			uint32 NumDependencies = 0;

			//0b11 for 2, 0b1111 for 4, and so on
			uint32 MultiviewMask = 0;//(0b1 << RTLayout.GetMultiViewCount()) - 1;

			const bool bDeferredShadingSubpass = false;
			
			const bool bCustomResolveSubpass = false;
			const bool bDepthReadSubpass = bCustomResolveSubpass || (RTLayout.GetSubpassHint() == ESubpassHint::DepthReadSubpass);
			const bool bHasDepthStencilAttachmentReference = (RTLayout.GetDepthAttachmentReference() != nullptr);

			// Grab (and optionally convert) attachment references.
			uint32 NumColorAttachments = 1;
			for (uint32 ColorAttachment = 0; ColorAttachment < NumColorAttachments; ++ColorAttachment)
			{
				ColorAttachmentReferences.push_back(TAttachmentReferenceClass(RTLayout.GetColorAttachmentReferences()[ColorAttachment], 0));
			}

			// CustomResolveSubpass has an additional color attachment that should not be used by main and depth subpasses
			if (bCustomResolveSubpass && (NumColorAttachments > 1))
			{
				NumColorAttachments--;
			}

			uint32_t DepthInputAttachment = VK_ATTACHMENT_UNUSED;
			VkImageLayout DepthInputAttachmentLayout = VK_IMAGE_LAYOUT_UNDEFINED;
			VkImageAspectFlags DepthInputAspectMask = 0;
			if (bHasDepthStencilAttachmentReference)
			{
				DepthStencilAttachmentReference.SetDepthStencilAttachment(*RTLayout.GetDepthAttachmentReference(), RTLayout.GetStencilAttachmentReference(), 0,false);// Device.SupportsParallelRendering());

				if (bDepthReadSubpass || bDeferredShadingSubpass)
				{
					DepthStencilAttachment.attachment = RTLayout.GetDepthAttachmentReference()->attachment;
					DepthStencilAttachment.SetAspect(VK_IMAGE_ASPECT_DEPTH_BIT);	// @todo?

					// FIXME: checking a Depth layout is not correct in all cases
					// PSO cache can create a PSO for subpass 1 or 2 first, where depth is read-only but that does not mean depth pre-pass is enabled
					if (false && RTLayout.GetDepthAttachmentReference()->layout == VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL)
					{
						// Depth is read only and is expected to be sampled as a regular texture
						DepthStencilAttachment.layout = VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL;
					}
					else
					{
						// lights write to stencil for culling, so stencil is expected to be writebale while depth is read-only
						DepthStencilAttachment.layout = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL;
						DepthInputAttachment = DepthStencilAttachment.attachment;
						DepthInputAttachmentLayout = DepthStencilAttachment.layout;
						DepthInputAspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
					}
				}
			}

			// main sub-pass
			{
				TSubpassDescriptionClass& SubpassDesc = SubpassDescriptions[NumSubpasses++];

				SubpassDesc.SetColorAttachments(ColorAttachmentReferences, NumColorAttachments);

				if (bHasDepthStencilAttachmentReference)
				{
					SubpassDesc.SetDepthStencilAttachment(&DepthStencilAttachmentReference);
				}
				SubpassDesc.SetMultiViewMask(MultiviewMask);
			}

			// Color write and depth read sub-pass
			if (bDepthReadSubpass)
			{
				TSubpassDescriptionClass& SubpassDesc = SubpassDescriptions[NumSubpasses++];

				SubpassDesc.SetColorAttachments(ColorAttachmentReferences, 1);

				// Depth as Input0
				InputAttachments1[0].attachment = DepthInputAttachment;
				InputAttachments1[0].layout = DepthInputAttachmentLayout;
				InputAttachments1[0].SetAspect(DepthInputAspectMask);
				SubpassDesc.SetInputAttachments(InputAttachments1, InputAttachment1Count);
				// depth attachment is same as input attachment
				SubpassDesc.SetDepthStencilAttachment(&DepthStencilAttachment);

				SubpassDesc.SetMultiViewMask(MultiviewMask);

				TSubpassDependencyClass& SubpassDep = SubpassDependencies[NumDependencies++];
				SubpassDep.srcSubpass = 0;
				SubpassDep.dstSubpass = 1;
				SubpassDep.srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
				SubpassDep.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
				SubpassDep.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
				SubpassDep.dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
				SubpassDep.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
			}

			// Two subpasses for deferred shading
			if (bDeferredShadingSubpass)
			{
				//const VkAttachmentReference* ColorRef = RTLayout.GetColorAttachmentReferences();
				//uint32 NumColorAttachments = RTLayout.GetNumColorAttachments();
				//check(RTLayout.GetNumColorAttachments() == 5); //current layout is SceneColor, GBufferA/B/C/D

				// 1. Write to SceneColor and GBuffer, input DepthStencil
				{
					TSubpassDescriptionClass& SubpassDesc = SubpassDescriptions[NumSubpasses++];
					SubpassDesc.SetColorAttachments(ColorAttachmentReferences);
					SubpassDesc.SetDepthStencilAttachment(&DepthStencilAttachment);
					InputAttachments1[0].attachment = DepthInputAttachment;
					InputAttachments1[0].layout = DepthInputAttachmentLayout;
					InputAttachments1[0].SetAspect(DepthInputAspectMask);
					SubpassDesc.SetInputAttachments(InputAttachments1, InputAttachment1Count);

					SubpassDesc.SetMultiViewMask(MultiviewMask);

					// Depth as Input0
					TSubpassDependencyClass& SubpassDep = SubpassDependencies[NumDependencies++];
					SubpassDep.srcSubpass = 0;
					SubpassDep.dstSubpass = 1;
					SubpassDep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
					SubpassDep.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
					SubpassDep.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
					SubpassDep.dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
					SubpassDep.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
				}

				// 2. Write to SceneColor, input GBuffer and DepthStencil
				{
					TSubpassDescriptionClass& SubpassDesc = SubpassDescriptions[NumSubpasses++];
					SubpassDesc.SetColorAttachments(ColorAttachmentReferences, 1); // SceneColor only
					SubpassDesc.SetDepthStencilAttachment(&DepthStencilAttachment);

					// Depth as Input0
					InputAttachments2[0].attachment = DepthInputAttachment;
					InputAttachments2[0].layout = DepthInputAttachmentLayout;
					InputAttachments2[0].SetAspect(DepthInputAspectMask);

					// SceneColor write only
					InputAttachments2[1].attachment = VK_ATTACHMENT_UNUSED;
					InputAttachments2[1].layout = VK_IMAGE_LAYOUT_UNDEFINED;
					InputAttachments2[1].SetAspect(0);

					// GBufferA/B/C/D as Input2/3/4/5
					int32 NumColorInputs = ColorAttachmentReferences.size() - 1;
					for (int32 i = 2; i < (NumColorInputs + 2); ++i)
					{
						InputAttachments2[i].attachment = ColorAttachmentReferences[i - 1].attachment;
						InputAttachments2[i].layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
						InputAttachments2[i].SetAspect(VK_IMAGE_ASPECT_COLOR_BIT);
					}

					SubpassDesc.SetInputAttachments(InputAttachments2, NumColorInputs + 2);
					SubpassDesc.SetMultiViewMask(MultiviewMask);

					TSubpassDependencyClass& SubpassDep = SubpassDependencies[NumDependencies++];
					SubpassDep.srcSubpass = 1;
					SubpassDep.dstSubpass = 2;
					SubpassDep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
					SubpassDep.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
					SubpassDep.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
					SubpassDep.dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
					SubpassDep.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
				}
			}

			// Custom resolve subpass
			if (bCustomResolveSubpass)
			{
			}

			// CustomResolveSubpass does a custom resolve into second color target and does not need resolve attachments
			if (!bCustomResolveSubpass)
			{
				// Only set resolve attachment on the last subpass
				SubpassDescriptions[NumSubpasses - 1].SetResolveAttachments(ResolveAttachmentReferences);
			}

			for (uint32 Attachment = 0; Attachment < RTLayout.GetNumAttachmentDescriptions(); ++Attachment)
			{
				if (bHasDepthStencilAttachmentReference && (Attachment == DepthStencilAttachmentReference.attachment))
				{
					AttachmentDescriptions.push_back(TAttachmentDescriptionClass(RTLayout.GetAttachmentDescriptions()[Attachment], RTLayout.GetStencilDesc(),false)); //Device.SupportsParallelRendering()));
				}
				else
				{
					AttachmentDescriptions.push_back(TAttachmentDescriptionClass(RTLayout.GetAttachmentDescriptions()[Attachment]));
				}
			}

			CreateInfo.attachmentCount = AttachmentDescriptions.size();
			CreateInfo.pAttachments = AttachmentDescriptions.data();
			CreateInfo.subpassCount = NumSubpasses;
			CreateInfo.pSubpasses = SubpassDescriptions;
			CreateInfo.dependencyCount = NumDependencies;
			CreateInfo.pDependencies = SubpassDependencies;

			/*
			Bit mask that specifies which view rendering is broadcast to
			0011 = Broadcast to first and second view (layer)
			*/
			ViewMask[0] = MultiviewMask;
			ViewMask[1] = MultiviewMask;

			/*
			Bit mask that specifices correlation between views
			An implementation may use this for optimizations (concurrent render)
			*/
			CorrelationMask = MultiviewMask;
		}

		VkRenderPass Create(const VulkanRenderTargetLayout& RTLayout)
		{
			BuildCreateInfo(RTLayout);
			return CreateInfo.Create(Device);
		}

		TRenderPassCreateInfoClass& GetCreateInfo()
		{
			return CreateInfo;
		}

	private:
		TSubpassDescriptionClass SubpassDescriptions[MaxSimultaneousRenderTargets];
		TSubpassDependencyClass SubpassDependencies[MaxSimultaneousRenderTargets];

		std::vector<TAttachmentReferenceClass> ColorAttachmentReferences;
		std::vector<TAttachmentReferenceClass> ResolveAttachmentReferences;

		// Color write and depth read sub-pass
		static const uint32 InputAttachment1Count = 1;
		TAttachmentReferenceClass InputAttachments1[InputAttachment1Count];

		// Two subpasses for deferred shading
		TAttachmentReferenceClass InputAttachments2[MaxSimultaneousRenderTargets + 1];
		TAttachmentReferenceClass DepthStencilAttachment;

		TAttachmentReferenceClass DepthStencilAttachmentReference;
		std::vector<TAttachmentDescriptionClass> AttachmentDescriptions;

		// Tonemap subpass
		TAttachmentReferenceClass InputAttachments3[MaxSimultaneousRenderTargets + 1];
		TAttachmentReferenceClass ColorAttachments3[MaxSimultaneousRenderTargets + 1];

		TRenderPassCreateInfoClass CreateInfo;
		VulkanDevice& Device;

		uint32 ViewMask[2];
		uint32 CorrelationMask;
	};

	VkRenderPass CreateVulkanRenderPass(VulkanDevice& Device, const VulkanRenderTargetLayout& RTLayout);
    class VulkanRenderPassManager
    {
    public:
        VulkanDevice* mDevice;
        VulkanRenderPassManager(VulkanDevice* InDevice) : mDevice(InDevice){}
        ~VulkanRenderPassManager();

        VulkanFramebuffer* GetOrCreateFramebuffer(const CGISetRenderTargetsInfo& RenderTargetsInfo, const VulkanRenderTargetLayout& RTLayout, VulkanRenderPass* RenderPass);
        VulkanRenderPass* GetOrCreateRenderPass(const VulkanRenderTargetLayout& RTLayout);

        void BeginRenderPass(FVulkanCommandListContext& Context, VulkanDevice& InDevice, VulkanCommandBuffer* CmdBuffer, const CGIRenderPassInfo& RPInfo, const VulkanRenderTargetLayout& RTLayout, VulkanRenderPass* RenderPass, VulkanFramebuffer* Framebuffer);
        void EndRenderPass(VulkanCommandBuffer* CmdBuffer);

        void NotifyDeletedRenderTarget(VkImage Image);

    private:
        std::list<VulkanRenderPass*> mRenderPasses;
        std::list<VulkanFramebuffer*> mFrameBuffers;
    };
}
